package com.bigbird.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

public class NioServerV4 {
    private static Work[] works;
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(1881));
        int workCount=1;
        works=new Work[workCount];
        for(int a=0;a<workCount;a++){
            works[a]= new Work();
        }
        for(int a=0;a<workCount;a++){
            Work work = works[a];
            new Thread(work).start();
        }
        new Thread(new Boss(serverSocketChannel)).start();
    }
    public static class Boss implements Runnable{
        private final ServerSocketChannel serverSocketChannel;
        public final Selector selector;

        public Boss(ServerSocketChannel serverSocketChannel) {
            this.serverSocketChannel = serverSocketChannel;
            try {
                this.serverSocketChannel.configureBlocking(false);
                this.selector=Selector.open();
                serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void run() {
             try {
                 while(true) {
                     try {
                         SocketChannel accept = serverSocketChannel.accept();
                         if (null != accept) {
                            getNextWork().register(accept);
                         }
                     }catch (IOException exception){
                         throw new RuntimeException(exception);
                     }
                 }
             } finally {
                 try {
                     selector.close();
                 } catch (IOException e) {
                     throw new RuntimeException(e);
                 }
             }
        }

        public Work getNextWork(){
            return works[0];
        }
    }

    public static class Work implements Runnable{
        private final Selector selector;

        public Work() {
            try {
                this.selector=Selector.open();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        public void register(SocketChannel socketChannel){
            try {
                if(socketChannel.isConnected()) {
                    socketChannel.configureBlocking(false);
                    socketChannel.register(this.selector, SelectionKey.OP_READ);
                    this.selector.wakeup();
                }
            } catch (Exception e) {
                try {
                    socketChannel.close();
                } catch (IOException ex) {
                }
            }
        }

        @Override
        public void run() {
            try{
                while(true){
                    selector.select();
                    Set<SelectionKey> keys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = keys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey selectionKey = iterator.next();
                        //清楚已处理的
                        keys.remove(selectionKey);
                        if (selectionKey.interestOps() == SelectionKey.OP_READ) {
                            try {
                                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                                if(selectionKey.isValid()&&selectionKey.isReadable()) {
                                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                                    int ret;
                                    int readbyte=0;
                                    while((ret=socketChannel.read(byteBuffer))>0) {
                                        readbyte+=ret;
                                        if(!byteBuffer.hasRemaining()){
                                            break;
                                        }
                                    }
                                    if(readbyte<0){
                                        ByteBuffer byteBufferWrite = ByteBuffer.wrap("good bye".getBytes(StandardCharsets.UTF_8));
                                        socketChannel.write(byteBufferWrite);
                                        selectionKey.cancel();
                                        socketChannel.socket().close();
                                    }else if(readbyte>0){
                                        byteBuffer.flip();
                                        byte[] bytes=new byte[readbyte];
                                        byteBuffer.get(bytes);
                                        String string = new String(bytes);
                                        System.out.println("socket:"+socketChannel+"  \r\n message:"+ string);
                                        ByteBuffer byteBufferWrite = ByteBuffer.wrap("good job".getBytes(StandardCharsets.UTF_8));
                                        socketChannel.write(byteBufferWrite);
                                        byteBuffer.rewind();
                                    }
                                }else{
                                    selectionKey.cancel();
                                }
                            }catch (Exception exception){
                                exception.printStackTrace();
                            }
                        }
                        else {
                            SelectableChannel channel = selectionKey.channel();
                            System.out.println(channel);
                            System.out.println(selectionKey.interestOps());
                        }

                    }
                }
            }catch (Exception exception){

            }finally {
                try {
                    selector.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

}
